home *** CD-ROM | disk | FTP | other *** search
/ Linux Cubed Series 7: Sunsite / Linux Cubed Series 7 - Sunsite Vol 1.iso / system / admin / shadow-9.tar / shadow-9 / shadow-960129 / login_access.c < prev    next >
C/C++ Source or Header  |  1995-12-17  |  8KB  |  266 lines

  1.  /*
  2.   * This module implements a simple but effective form of login access
  3.   * control based on login names and on host (or domain) names, internet
  4.   * addresses (or network numbers), or on terminal line names in case of
  5.   * non-networked logins. Diagnostics are reported through syslog(3).
  6.   * 
  7.   * Author: Wietse Venema, Eindhoven University of Technology, The Netherlands.
  8.   */
  9.  
  10. #ifndef lint
  11. static char rcsid[] = "$Id: login_access.c,v 1.1 1995/12/16 01:15:45 marekm Exp $";
  12. #endif
  13.  
  14. #include <sys/types.h>
  15. #include <stdio.h>
  16. #include <syslog.h>
  17. #include <ctype.h>
  18. #include <grp.h>
  19. #include <errno.h>
  20. #include <string.h>
  21. #include <unistd.h>
  22. #include <stdlib.h>
  23.  
  24. #include "sys_defs.h"
  25.  
  26. extern struct group *getgrnam();
  27. extern int errno;
  28.  
  29. #if !defined(MAXHOSTNAMELEN) || (MAXHOSTNAMELEN < 64)
  30. #undef MAXHOSTNAMELEN
  31. #define MAXHOSTNAMELEN 256
  32. #endif
  33.  
  34.  /* Path name of the access control file. */
  35.  
  36. #ifndef    TABLE
  37. #define TABLE    "/etc/login.access"
  38. #endif
  39.  
  40.  /* Delimiters for fields and for lists of users, ttys or hosts. */
  41.  
  42. static char fs[] = ":";            /* field separator */
  43. static char sep[] = ", \t";        /* list-element separator */
  44.  
  45.  /* Constants to be used in assignments only, not in comparisons... */
  46.  
  47. #define YES             1
  48. #define NO              0
  49.  
  50. static int list_match();
  51. static int user_match();
  52. static int from_match();
  53. static int string_match();
  54.  
  55. /* login_access - match username/group and host/tty with access control file */
  56.  
  57. login_access(user, from)
  58. char   *user;
  59. char   *from;
  60. {
  61.     FILE   *fp;
  62.     char    line[BUFSIZ];
  63.     char   *perm;            /* becomes permission field */
  64.     char   *users;            /* becomes list of login names */
  65.     char   *froms;            /* becomes list of terminals or hosts */
  66.     int     match = NO;
  67.     int     end;
  68.     int     lineno = 0;            /* for diagnostics */
  69.  
  70.     /*
  71.      * Process the table one line at a time and stop at the first match.
  72.      * Blank lines and lines that begin with a '#' character are ignored.
  73.      * Non-comment lines are broken at the ':' character. All fields are
  74.      * mandatory. The first field should be a "+" or "-" character. A
  75.      * non-existing table means no access control.
  76.      */
  77.  
  78.     if (fp = fopen(TABLE, "r")) {
  79.     while (!match && fgets(line, sizeof(line), fp)) {
  80.         lineno++;
  81.         if (line[end = strlen(line) - 1] != '\n') {
  82.         syslog(LOG_ERR, "%s: line %d: missing newline or line too long",
  83.                TABLE, lineno);
  84.         continue;
  85.         }
  86.         if (line[0] == '#')
  87.         continue;            /* comment line */
  88.         while (end > 0 && isspace(line[end - 1]))
  89.         end--;
  90.         line[end] = 0;            /* strip trailing whitespace */
  91.         if (line[0] == 0)            /* skip blank lines */
  92.         continue;
  93.         if (!(perm = strtok(line, fs))
  94.         || !(users = strtok((char *) 0, fs))
  95.         || !(froms = strtok((char *) 0, fs))
  96.         || strtok((char *) 0, fs)) {
  97.         syslog(LOG_ERR, "%s: line %d: bad field count", TABLE, lineno);
  98.         continue;
  99.         }
  100.         if (perm[0] != '+' && perm[0] != '-') {
  101.         syslog(LOG_ERR, "%s: line %d: bad first field", TABLE, lineno);
  102.         continue;
  103.         }
  104.         match = (list_match(froms, from, from_match)
  105.              && list_match(users, user, user_match));
  106.     }
  107.     (void) fclose(fp);
  108.     } else if (errno != ENOENT) {
  109.     syslog(LOG_ERR, "cannot open %s: %m", TABLE);
  110.     }
  111.     return (match == 0 || (line[0] == '+'));
  112. }
  113.  
  114. /* list_match - match an item against a list of tokens with exceptions */
  115.  
  116. static int list_match(list, item, match_fn)
  117. char   *list;
  118. char   *item;
  119. int   (*match_fn) ();
  120. {
  121.     char   *tok;
  122.     int     match = NO;
  123.  
  124.     /*
  125.      * Process tokens one at a time. We have exhausted all possible matches
  126.      * when we reach an "EXCEPT" token or the end of the list. If we do find
  127.      * a match, look for an "EXCEPT" list and recurse to determine whether
  128.      * the match is affected by any exceptions.
  129.      */
  130.  
  131.     for (tok = strtok(list, sep); tok != 0; tok = strtok((char *) 0, sep)) {
  132.     if (strcasecmp(tok, "EXCEPT") == 0)    /* EXCEPT: give up */
  133.         break;
  134.     if (match = (*match_fn) (tok, item))    /* YES */
  135.         break;
  136.     }
  137.     /* Process exceptions to matches. */
  138.  
  139.     if (match != NO) {
  140.     while ((tok = strtok((char *) 0, sep)) && strcasecmp(tok, "EXCEPT"))
  141.          /* VOID */ ;
  142.     if (tok == 0 || list_match((char *) 0, item, match_fn) == NO)
  143.         return (match);
  144.     }
  145.     return (NO);
  146. }
  147.  
  148. /* myhostname - figure out local machine name */
  149.  
  150. static char *myhostname()
  151. {
  152.     static char name[MAXHOSTNAMELEN + 1] = "";
  153.  
  154.     if (name[0] == 0) {
  155.     gethostname(name, sizeof(name));
  156.     name[MAXHOSTNAMELEN] = 0;
  157.     }
  158.     return (name);
  159. }
  160.  
  161. /* netgroup_match - match group against machine or user */
  162.  
  163. static int netgroup_match(group, machine, user)
  164. char   *group;
  165. char   *machine;
  166. char   *user;
  167. {
  168. #ifdef NIS
  169.     static char *mydomain = 0;
  170.  
  171.     if (mydomain == 0)
  172.     yp_get_default_domain(&mydomain);
  173.     return (innetgr(group, machine, user, mydomain));
  174. #else
  175.     syslog(LOG_ERR, "NIS netgroup support not configured");
  176.     return (NO);
  177. #endif
  178. }
  179.  
  180. /* user_match - match a username against one token */
  181.  
  182. static int user_match(tok, string)
  183. char   *tok;
  184. char   *string;
  185. {
  186.     struct group *group;
  187.     int     i;
  188.     char   *at;
  189.  
  190.     /*
  191.      * If a token has the magic value "ALL" the match always succeeds.
  192.      * Otherwise, return YES if the token fully matches the username, or if
  193.      * the token is a group that contains the username.
  194.      */
  195.  
  196.     if ((at = strchr(tok + 1, '@')) != 0) {    /* split user@host pattern */
  197.     *at = 0;
  198.     return (user_match(tok, string) && from_match(at + 1, myhostname()));
  199.     } else if (tok[0] == '@') {            /* netgroup */
  200.     return (netgroup_match(tok + 1, (char *) 0, string));
  201.     } else if (string_match(tok, string)) {    /* ALL or exact match */
  202.     return (YES);
  203.     } else if (group = getgrnam(tok)) {        /* try group membership */
  204.     for (i = 0; group->gr_mem[i]; i++)
  205.         if (strcasecmp(string, group->gr_mem[i]) == 0)
  206.         return (YES);
  207.     }
  208.     return (NO);
  209. }
  210.  
  211. /* from_match - match a host or tty against a list of tokens */
  212.  
  213. static int from_match(tok, string)
  214. char   *tok;
  215. char   *string;
  216. {
  217.     int     tok_len;
  218.     int     str_len;
  219.  
  220.     /*
  221.      * If a token has the magic value "ALL" the match always succeeds. Return
  222.      * YES if the token fully matches the string. If the token is a domain
  223.      * name, return YES if it matches the last fields of the string. If the
  224.      * token has the magic value "LOCAL", return YES if the string does not
  225.      * contain a "." character. If the token is a network number, return YES
  226.      * if it matches the head of the string.
  227.      */
  228.  
  229.     if (tok[0] == '@') {            /* netgroup */
  230.     return (netgroup_match(tok + 1, string, (char *) 0));
  231.     } else if (string_match(tok, string)) {    /* ALL or exact match */
  232.     return (YES);
  233.     } else if (tok[0] == '.') {            /* domain: match last fields */
  234.     if ((str_len = strlen(string)) > (tok_len = strlen(tok))
  235.         && strcasecmp(tok, string + str_len - tok_len) == 0)
  236.         return (YES);
  237.     } else if (strcasecmp(tok, "LOCAL") == 0) {    /* local: no dots */
  238.     if (strchr(string, '.') == 0)
  239.         return (YES);
  240.     } else if (tok[(tok_len = strlen(tok)) - 1] == '.'    /* network */
  241.            && strncmp(tok, string, tok_len) == 0) {
  242.     return (YES);
  243.     }
  244.     return (NO);
  245. }
  246.  
  247. /* string_match - match a string against one token */
  248.  
  249. static int string_match(tok, string)
  250. char   *tok;
  251. char   *string;
  252. {
  253.  
  254.     /*
  255.      * If the token has the magic value "ALL" the match always succeeds.
  256.      * Otherwise, return YES if the token fully matches the string.
  257.      */
  258.  
  259.     if (strcasecmp(tok, "ALL") == 0) {        /* all: always matches */
  260.     return (YES);
  261.     } else if (strcasecmp(tok, string) == 0) {    /* try exact match */
  262.     return (YES);
  263.     }
  264.     return (NO);
  265. }
  266.